/*
 * np_stats.h
 *
 *  Created on: Mar 24, 2011
 *      Author: jeff_zheng
 *   Copyright: NetPerform Technology
 */

#ifndef NP_STATS_H_
#define NP_STATS_H_

#ifdef __cplusplus
extern "C" {
#endif


/**
 * @defgroup np_stats_acc np_stats_acc
 * @brief a quick stats accumulator
 * @ingroup np_common
 *
 * used to monitor a incremental value and create interval values from historical values
 * the accumulator can monitor a set of instance, and each instance can have multiple fields
 */

/*
 *
 */
typedef enum np_stats_acc_val_e
{
	NP_STATS_ACC_VAL_INTERVAL = 0,
	NP_STATS_ACC_VAL_CURRENT  = 1
}np_stats_acc_value;

typedef enum np_stats_acc_type_e
{
	NP_STATS_ACC_UNK = 0,
	NP_STATS_ACC_MAX = 1,
	NP_STATS_ACC_MIN = 2,
	NP_STATS_ACC_MEAN = 3,
	NP_STATS_ACC_SUM = 4,
}np_stats_acc_type;

typedef struct np_stats_acc_unit_s
{
	uint8_t acc_data[8];
	uint8_t raw_data[8];
	uint64_t count;
}np_stats_acc_unit;

typedef struct np_stats_acc_s
{
	np_stats_acc_value 	type;
	uint32_t 			instance_cnt;
	uint32_t 			field_cnt;
	np_stats_acc_type*	acc_types;

	np_stats_acc_unit*	acc_data;
	void* 				old_value;
	char* 				interval_init;
}np_stats_acc;

/**
 * @ingroup np_stats_acc
 * @brief Uninitialize a stats accumulator
 *
 * @param[in] p_acc 	        struct to initialize
 * @return 0 for success, non-zero for Error
 *
 * Uninitialize a stats accumulator, free all resources.
 **/
static inline void np_stats_acc_destroy(np_stats_acc* p_acc)
{
	if(p_acc)
	{
		free(p_acc);
	}
}
/**
 * @ingroup np_stats_acc
 * @brief initialize a stats accumulator
 *
 * @param[in] p_acc 	        struct to initialize
 * @param[in] instance_count 	number of instances
 * @param[in] field_count 	    number of fields per instance
 * @return the created stats_acc obj, NULL for Error
 *
 **/
static inline np_stats_acc* np_stats_acc_create(np_stats_acc_value type, uint32_t instance_count, np_stats_acc_type* field_acc_types, uint32_t field_count)
{
	uint32_t total_size = sizeof(np_stats_acc) +
						sizeof(np_stats_acc_type)*field_count +
						sizeof(uint64_t)*(instance_count*field_count*2) +
						sizeof(np_stats_acc_unit)*(instance_count*field_count);
	np_stats_acc* p_acc = (np_stats_acc*)malloc(total_size);
	if(p_acc==NULL)
		return NULL;
	memset(p_acc, 0, total_size);
	p_acc->type = type;
	p_acc->instance_cnt = instance_count;
	p_acc->field_cnt = field_count;
	p_acc->old_value = NP_PTR_OFFSET(p_acc, sizeof(*p_acc), void);
	p_acc->acc_types = NP_PTR_OFFSET(p_acc->old_value,  (instance_count*field_count*sizeof(uint64_t)), np_stats_acc_type);
	memcpy(p_acc->acc_types, field_acc_types, sizeof(np_stats_acc_type)* field_count);
	p_acc->acc_data = NP_PTR_OFFSET(p_acc->acc_types, sizeof(np_stats_acc_type)*(field_count), np_stats_acc_unit);
	p_acc->interval_init = NP_PTR_OFFSET(p_acc->acc_data, (instance_count*field_count*sizeof(np_stats_acc_unit)), char);
	return p_acc;
}


/**
 * @ingroup np_stats_acc
 * @brief set initial value for off field
 *
 * @param[in] p_acc 	        acc struct
 * @param[in] inst 				instance index
 * @param[in] field 	    	field index
 * @param[in] value 	    	initial value
 * @return 0 for success, non-zero for Error
 *
 * after initialization all fields are set to 0, but the user data may already have an existing value.
 * In such case, the caller should call this function once per instance per field with the existing value.
 **/
//static inline int np_stats_acc_set_initial_val(np_stats_acc* p_acc, uint32_t inst, uint32_t field, uint64_t value)
//{
////	*(p_acc->old_value+(inst*p_acc->field_cnt + field)) = value;
//	return 0;
//}

/**
 * @ingroup np_stats_acc
 * @brief set initial value for off field
 *
 * @param[in] p_acc 	        acc struct
 * @param[in] inst 				instance index
 * @param[in] field 	    	field index
 * @param[in] value 	    	new value to be pushed
 * @return 0 for success, non-zero for Error
 *
 * the caller should call this function periodically with a new value each time.
 * the accumulator will then use the new value to generate the value the caller needed.
 * In such case, the caller should call this function once per instance per field with the existing value.
 **/
//static inline uint64_t np_stats_acc_push_new(np_stats_acc* p_acc, uint32_t inst, uint32_t field, uint64_t value)
//{
//	uint32_t idx = (inst*p_acc->field_cnt) + field;
//	uint64_t v = value - (*(p_acc->old_value+idx));
//	*(p_acc->interval_value+idx) = v;
//	*(p_acc->old_value+idx) = value;
//	return 0;
//}

/**
 * @ingroup np_stats_acc
 * @brief get the last generated value
 *
 * @param[in] p_acc 	        acc struct
 * @param[in] inst 				instance index
 * @param[in] field 	    	field index
 * @return the last generated value, if no value has been generated, 0 is returned
 *
 * the caller should call this function periodically with a new value each time.
 * the accumulator will then use the new value to generate the value the caller needed.
 * In such case, the caller should call this function once per instance per field with the existing value.
 **/
//static inline uint64_t np_stats_acc_get_val(np_stats_acc* p_acc, uint32_t inst, uint32_t field)
//{
//	switch(p_acc->type)
//	{
//	case NP_STATS_ACC_VAL_INTERVAL:
//		return *(p_acc->interval_value+(inst*p_acc->field_cnt + field));
//	case NP_STATS_ACC_VAL_CURRENT:
//		return *(p_acc->old_value+(inst*p_acc->field_cnt + field));
//	}
//	return 0;
//}

#define np_stats_acc_get_val_def(data_type) \


np_stats_acc_get_val_def(float);
np_stats_acc_get_val_def(double);
np_stats_acc_get_val_def(uint32_t);
np_stats_acc_get_val_def(uint64_t);

#define np_stats_acc_type_def(data_type, name_type) \
static inline void np_stats_acc_cal_acc_## name_type ( np_stats_acc* p_acc, uint32_t inst, uint32_t field, data_type new_value) \
{\
	uint32_t idx = (inst*p_acc->field_cnt) + field; \
	np_stats_acc_type acc_type = p_acc->acc_types[field]; \
	np_stats_acc_unit* p_unit = p_acc->acc_data + idx;\
	data_type* p_raw = (data_type*)(p_unit->raw_data); \
	data_type* p_ret = (data_type*)(p_unit->acc_data); \
	p_unit->count++; \
	if(p_unit->count==1) { \
		*p_raw = new_value; \
		*p_ret = (*p_raw); \
	} \
	else \
	{ \
		switch(acc_type) \
		{\
		case 	NP_STATS_ACC_UNK:  \
			printf("unknow acc type"); \
			break; \
		case 	NP_STATS_ACC_MAX:  \
			if(*p_raw < new_value)	{ \
				*p_raw = new_value; \
			} \
			*p_ret = (*p_raw); \
			break; \
		case 	NP_STATS_ACC_MIN: \
			if(*p_raw > new_value)	{ \
				*p_raw = new_value; \
			} \
			*p_ret = (*p_raw); \
			break; \
		case 	NP_STATS_ACC_MEAN: \
			*p_raw += new_value; \
			*p_ret = (*p_raw) / p_unit->count; \
			break; \
		case 	NP_STATS_ACC_SUM: \
			*p_raw += new_value; \
			*p_ret = (*p_raw); \
			break; \
		} \
	}\
\
}\
static inline void np_stats_acc_push_## name_type(np_stats_acc* p_acc, uint32_t inst, uint32_t field, data_type value) \
{ \
	uint32_t idx = (inst*p_acc->field_cnt) + field; \
	data_type v = value;\
	if(p_acc->type==NP_STATS_ACC_VAL_INTERVAL)\
	{ /*interval, we should calculate the interval value here*/\
		if(p_acc->interval_init[idx]==0) \
		{/*one time init for a single field*/\
			p_acc->interval_init[idx] = 1;\
			/*initialize old_value, and not calling the cal_acc functions*/ \
			*NP_PTR_OFFSET(p_acc->old_value, idx*sizeof(uint64_t), data_type) = value; \
			return; \
		}\
		v = value - (*NP_PTR_OFFSET(p_acc->old_value, idx*sizeof(uint64_t), data_type)); \
	} \
	*NP_PTR_OFFSET(p_acc->old_value, idx*sizeof(uint64_t), data_type) = value; \
	np_stats_acc_cal_acc_## name_type(p_acc, inst, field, v); \
\
	return; \
} \
static inline data_type np_stats_acc_get_val_##name_type (np_stats_acc* p_acc, uint32_t inst, uint32_t field) \
{ \
	void* p_data = (p_acc->acc_data+(inst*p_acc->field_cnt + field))->acc_data; \
		return *(data_type*)p_data;\
}

static inline void np_stats_acc_clear(np_stats_acc* p_acc)
{
	memset(p_acc->acc_data, 0, sizeof(np_stats_acc_unit)*p_acc->instance_cnt*p_acc->field_cnt);
}


np_stats_acc_type_def(float, float);
np_stats_acc_type_def(double, double);
np_stats_acc_type_def(uint32_t, uint32);
np_stats_acc_type_def(int32_t, int32);
np_stats_acc_type_def(int64_t, int64);
np_stats_acc_type_def(uint64_t, uint64);


#ifdef __cplusplus
}
#endif

#endif /* NP_STATS_H_ */
